﻿using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Data;
using System.Xml;
using Microsoft.VisualStudio.PlatformUI;

namespace Red.Scripts.Shell.AboutBoxPackage
{
    /// <summary>
    /// Interaction logic for AboutBox.xaml
    /// </summary>
    public partial class AboutBox : DialogWindow
    {
        /// <summary>
        /// Default constructor.
        /// </summary>
        public AboutBox()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Handles click navigation on the hyperlink in the About dialog.
        /// </summary>
        /// <param name="sender">Object that sent the event.</param>
        /// <param name="e">Navigation events arguments.</param>
        private void hyperlink_RequestNavigate( object sender, System.Windows.Navigation.RequestNavigateEventArgs e )
        {
            if ( e.Uri != null && string.IsNullOrEmpty( e.Uri.OriginalString ) == false )
            {
                string uri = e.Uri.AbsoluteUri;
                Process.Start( new ProcessStartInfo( uri ) );
                e.Handled = true;
            }
        }

        #region AboutData Provider
        #region Member data
        private XmlDocument xmlDoc = null;

        private const string propertyNameTitle = "Title";
        private const string propertyNameDescription = "Description";
        private const string propertyNameProduct = "Product";
        private const string propertyNameCopyright = "Copyright";
        private const string propertyNameCompany = "Company";
        private const string xPathRoot = "ApplicationInfo/";
        private const string xPathTitle = xPathRoot + propertyNameTitle;
        private const string xPathVersion = xPathRoot + "Version";
        private const string xPathDescription = xPathRoot + propertyNameDescription;
        private const string xPathProduct = xPathRoot + propertyNameProduct;
        private const string xPathCopyright = xPathRoot + propertyNameCopyright;
        private const string xPathCompany = xPathRoot + propertyNameCompany;
        private const string xPathLink = xPathRoot + "Link";
        private const string xPathLinkUri = xPathRoot + "Link/@Uri";
        #endregion

        #region Properties
        /// <summary>
        /// Gets the title property, which is display in the About dialogs window title.
        /// </summary>
        public string ProductTitle
        {
            get
            {
                string result = CalculatePropertyValue<AssemblyTitleAttribute>( propertyNameTitle, xPathTitle );
                if ( string.IsNullOrEmpty( result ) )
                {
                    // otherwise, just get the name of the assembly itself.
                    result = Path.GetFileNameWithoutExtension( Assembly.GetExecutingAssembly().CodeBase );
                }
                return result;
            }
        }

        /// <summary>
        /// Gets the application's version information to show.
        /// </summary>
        public string Version
        {
            get
            {
                string result = string.Empty;
                // first, try to get the version string from the assembly.
                Version version = Assembly.GetExecutingAssembly().GetName().Version;
                if ( version != null )
                {
                    result = version.ToString();
                }
                else
                {
                    // if that fails, try to get the version from a resource in the Application.
                    result = GetLogicalResourceString( xPathVersion );
                }
                return result;
            }
        }

        /// <summary>
        /// Gets the description about the application.
        /// </summary>
        public string Description
        {
            get { return CalculatePropertyValue<AssemblyDescriptionAttribute>( propertyNameDescription, xPathDescription ); }
        }

        /// <summary>
        ///  Gets the product's full name.
        /// </summary>
        public string Product
        {
            get { return CalculatePropertyValue<AssemblyProductAttribute>( propertyNameProduct, xPathProduct ); }
        }

        /// <summary>
        /// Gets the copyright information for the product.
        /// </summary>
        public string Copyright
        {
            get { return CalculatePropertyValue<AssemblyCopyrightAttribute>( propertyNameCopyright, xPathCopyright ); }
        }

        /// <summary>
        /// Gets the product's company name.
        /// </summary>
        public string Company
        {
            get { return CalculatePropertyValue<AssemblyCompanyAttribute>( propertyNameCompany, xPathCompany ); }
        }

        /// <summary>
        /// Gets the link text to display in the About dialog.
        /// </summary>
        public string LinkText
        {
            get { return GetLogicalResourceString( xPathLink ); }
        }

        /// <summary>
        /// Gets the link uri that is the navigation target of the link.
        /// </summary>
        public string LinkUri
        {
            get { return GetLogicalResourceString( xPathLinkUri ); }
        }
        #endregion

        #region Resource location methods
        /// <summary>
        /// Gets the specified property value either from a specific attribute, or from a resource dictionary.
        /// </summary>
        /// <typeparam name="T">Attribute type that we're trying to retrieve.</typeparam>
        /// <param name="propertyName">Property name to use on the attribute.</param>
        /// <param name="xpathQuery">XPath to the element in the XML data resource.</param>
        /// <returns>The resulting string to use for a property.
        /// Returns null if no data could be retrieved.</returns>
        private string CalculatePropertyValue<T>( string propertyName, string xpathQuery )
        {
            string result = string.Empty;
            // first, try to get the property value from an attribute.
            object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes( typeof( T ), false );
            if ( attributes.Length > 0 )
            {
                T attrib = (T)attributes[ 0 ];
                PropertyInfo property = attrib.GetType().GetProperty( propertyName, BindingFlags.Public | BindingFlags.Instance );
                if ( property != null )
                {
                    result = property.GetValue( attributes[ 0 ], null ) as string;
                }
            }

            // if the attribute wasn't found or it did not have a value, then look in an xml resource.
            if ( result == string.Empty )
            {
                // if that fails, try to get it from a resource.
                result = GetLogicalResourceString( xpathQuery );
            }
            return result;
        }

        /// <summary>
        /// Gets the XmlDataProvider's document from the resource dictionary.
        /// </summary>
        protected virtual XmlDocument ResourceXmlDocument
        {
            get
            {
                if ( xmlDoc == null )
                {
                    // if we haven't already found the resource XmlDocument, then try to find it.
                    XmlDataProvider provider = this.TryFindResource( "aboutProvider" ) as XmlDataProvider;
                    if ( provider != null )
                    {
                        // save away the XmlDocument, so we don't have to get it multiple times.
                        xmlDoc = provider.Document;
                    }
                }
                return xmlDoc;
            }
        }

        /// <summary>
        /// Gets the specified data element from the XmlDataProvider in the resource dictionary.
        /// </summary>
        /// <param name="xpathQuery">An XPath query to the XML element to retrieve.</param>
        /// <returns>The resulting string value for the specified XML element. 
        /// Returns empty string if resource element couldn't be found.</returns>
        protected virtual string GetLogicalResourceString( string xpathQuery )
        {
            string result = string.Empty;
            // get the About xml information from the resources.
            XmlDocument doc = this.ResourceXmlDocument;
            if ( doc != null )
            {
                // if we found the XmlDocument, then look for the specified data. 
                XmlNode node = doc.SelectSingleNode( xpathQuery );
                if ( node != null )
                {
                    if ( node is XmlAttribute )
                    {
                        // only an XmlAttribute has a Value set.
                        result = node.Value;
                    }
                    else
                    {
                        // otherwise, need to just return the inner text.
                        result = node.InnerText;
                    }
                }
            }
            return result;
        }
        #endregion
        #endregion
    }
}
